package com.gandong8.tablestore.tool.service.impl;

import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.model.*;
import com.gandong8.tablestore.tool.service.inter.TablestoreService;
import com.gandong8.tablestore.tool.util.TableStoreUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * Java SDK: https://help.aliyun.com/document_detail/99354.html
 * 全局二级索引: https://help.aliyun.com/document_detail/99106.html
 * 多元索引：https://help.aliyun.com/document_detail/100309.html
 * 表设计：https://help.aliyun.com/document_detail/142533.html
 */
@Service
@Slf4j
public class TablestoreServiceImpl implements TablestoreService {

    @Autowired
    private SyncClient syncClient;

    @Override
    public Boolean createTable(Class clazz) {
        try {
            syncClient.createTable(TableStoreUtil.buildCreateTable(clazz));
            return true;
        } catch (Exception e) {
            log.error("createTableException:{}", clazz, e);
        }
        return false;
    }




    @Override
    public Boolean deleteTable(Class clazz) {
        try {
            DeleteTableRequest request = new DeleteTableRequest(TableStoreUtil.getTableName(clazz));
            syncClient.deleteTable(request);
            return true;
        } catch (Exception e) {
            log.error("deleteTableException:{}", clazz, e);
        }
        return false;
    }

    @Override
    public <T> T getData(Object obj) {
        Class clazz = obj.getClass();
        try {
            PrimaryKey primaryKey = TableStoreUtil.parsePrimaryKey(obj);
            if (primaryKey == null) {
                return null;
            }
            SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TableStoreUtil.getTableName(clazz), primaryKey);
            //读取最新版本
            criteria.setMaxVersions(1);
            TableStoreUtil.buildQuery(clazz, criteria);
            GetRowResponse getRowResponse = syncClient.getRow(new GetRowRequest(criteria));
            if(getRowResponse == null || getRowResponse.getRow()==null) {
                return null;
            }
            return TableStoreUtil.parseRow(clazz, getRowResponse.getRow());
        } catch (Exception e) {
            log.error("getRowException:{}", obj, e);
        }
        return null;
    }

    @Override
    public boolean deleteData(Object obj) {
        try {
            RowDeleteChange rowDeleteChange = new RowDeleteChange(TableStoreUtil.getTableName(obj.getClass()), TableStoreUtil.parsePrimaryKey(obj));
            DeleteRowResponse deleteRowResponse = syncClient.deleteRow(new DeleteRowRequest(rowDeleteChange));
            if (deleteRowResponse != null) {
                return true;
            }
        } catch (Exception e) {
            log.error("deleteDataException:{}", obj, e);
        }
        return false;
    }

    @Override
    public boolean updateData(Object obj) {
        try {
            RowUpdateChange rowUpdateChange = TableStoreUtil.buildUpdate(obj);
            UpdateRowResponse updateRowResponse = syncClient.updateRow(new UpdateRowRequest(rowUpdateChange));
            if (updateRowResponse != null) {
                return true;
            }
        } catch (Exception e) {
            log.error("updateDataException:{}", obj, e);
        }
        return false;
    }

    @Override
    public boolean putData(Object obj) {
        try {
            RowPutChange rowPutChange = TableStoreUtil.buildPut(obj);
            rowPutChange.setReturnType(ReturnType.RT_PK);
            PutRowResponse putRowResponse = syncClient.putRow(new PutRowRequest(rowPutChange));
            if (putRowResponse != null) {
                return true;
            }
        } catch (Exception e) {
            log.error("putDataException:{}", obj, e);
        }
        return false;
    }

    @Override
    public <T> void batchPutData(List<T> objectList) {
        if (CollectionUtils.isEmpty(objectList)) {
            return ;
        }
        if(objectList.size() > 100) {
            //todo
        }
        try {
            BatchWriteRowRequest batchWriteRowRequest = new BatchWriteRowRequest();
            for (T obj : objectList) {
                RowPutChange rowPutChange = TableStoreUtil.buildPut(obj);
                batchWriteRowRequest.addRowChange(rowPutChange);
            }
            BatchWriteRowResponse response = syncClient.batchWriteRow(batchWriteRowRequest);
            int tryNum = 0;
            int totalRetry = 3;
            while (!response.isAllSucceed() && tryNum < totalRetry) {
                BatchWriteRowRequest retryRequest = batchWriteRowRequest.createRequestForRetry(response.getFailedRows());
                response = syncClient.batchWriteRow(retryRequest);
                tryNum++;
                if (tryNum >= totalRetry && !response.isAllSucceed()) {
                    log.error("batchPutRowTooMuch:{}", response.getFailedRows());
                }
            }
        } catch (Exception e) {
            log.error("batchPutDataException:{}", objectList, e);
        }
    }


    @Override
    public  <T> List<T> batchGetData(List<T> objectList) {
        List<T> newObjList = new ArrayList<>();
        if (CollectionUtils.isEmpty(objectList)) {
            return newObjList;
        }
        try {
            Class clazz = objectList.get(0).getClass();
            MultiRowQueryCriteria multiRowQueryCriteria = new MultiRowQueryCriteria(TableStoreUtil.getTableName(clazz));
            multiRowQueryCriteria.setMaxVersions(1);
            TableStoreUtil.buildQuery(clazz, multiRowQueryCriteria);
            for (T obj : objectList) {
                multiRowQueryCriteria.addRow(TableStoreUtil.parsePrimaryKey(obj));
            }
            BatchGetRowRequest batchGetRowRequest = new BatchGetRowRequest();
            batchGetRowRequest.addMultiRowQueryCriteria(multiRowQueryCriteria);
            BatchGetRowResponse batchGetRowResponse = syncClient.batchGetRow(batchGetRowRequest);
            if(CollectionUtils.isEmpty(batchGetRowResponse.getSucceedRows())) {
                return newObjList;
            }
            List<BatchGetRowResponse.RowResult> rowResults = batchGetRowResponse.getSucceedRows();
            for(BatchGetRowResponse.RowResult rowResult:rowResults) {
                if(rowResult.getRow() == null) {
                    continue;
                }
                newObjList.add(TableStoreUtil.parseRow(clazz, rowResult.getRow()));
            }
        } catch (Exception e) {
            log.error("batchGetDataException:{}", objectList, e);
        }
        return newObjList;
    }


    @Override
    public <T> List<T> getRangeData(T begin, T end) {
        List<T> objList = new ArrayList<>();
        try {
            if (begin == null || end == null) {
                return objList;
            }
            PrimaryKey startPrimaryKey = TableStoreUtil.parsePrimaryKey(begin);
            PrimaryKey endPrimaryKey = TableStoreUtil.parsePrimaryKey(end);
            if (startPrimaryKey == null || endPrimaryKey == null) {
                return objList;
            }
            Class<?> clazz = begin.getClass();
            RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(TableStoreUtil.getTableName(clazz));
            rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKey);
            rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKey);
            rangeRowQueryCriteria.setMaxVersions(1);
            while (true) {
                GetRangeResponse getRangeResponse = syncClient.getRange(new GetRangeRequest(rangeRowQueryCriteria));
                for (Row row : getRangeResponse.getRows()) {
                    T rowObj = TableStoreUtil.parseRow(clazz, row);
                    if (rowObj == null) {
                        continue;
                    }
                    objList.add(rowObj);
                }
                if(getRangeResponse.getNextStartPrimaryKey() == null) {
                    break;
                }
                rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
            }
        } catch (Exception e) {
            log.error("getRangeException:{},{}", begin,end ,e);
        }
        return objList;
    }













}
